package com.ruoyi.projects.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.WxAliConstants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.ApiResult;
import com.ruoyi.common.exception.AppException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.AccessToken;
import com.ruoyi.common.utils.http.HttpsUtils;
import com.ruoyi.projects.Vos.WebWxUserInfo;
import com.ruoyi.projects.Vos.WxUserInfoDTO;
import com.ruoyi.projects.domain.WechatConfig;
import com.ruoyi.projects.service.IWxAuthTokenService;
import com.ruoyi.projects.utils.WechatConfigUtils;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Service;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
public class WxAuthTokenServiceImpl implements IWxAuthTokenService {

    /**
     * 网页授权获取code
     */
    @Override
    public void getCode() {
        WechatConfig wechatCache = WechatConfigUtils.getWechatCache(WxAliConstants.REDIS_WECHAT_CONFIG);
        //非静默授权，获取用户信息
        String state="123456";//重定向后会带上state参数，开发者可以填写a-zA-Z0-9的参数值，最多128字节
        String info_codeUrl= String.format(WxAliConstants.WEB_AUTH_CODE_URL_userinfo,wechatCache.getMiniAppId(),WxAliConstants.CODE_LOCAL_NOTIFY_URL,state);
        HttpsUtils.httpsRequest(info_codeUrl, "GET", null);

        //静默授权
//        String base_codeUrl= String.format(WxAliConstants.WEB_AUTH_CODE_URL_base,"appid","redirect_uri","state");
//        HttpsUtils.httpsRequest(base_codeUrl, "GET", null);
//          ………………
    }

    /**
     * 用code换取网页授权accessToken
     * @param code
     * @return
     */
    @Override
    public Map<String, Object> getWebAccessToken(String code) {
        Map<String,Object> resultMap=new HashMap<>();
        WechatConfig wechatCache = WechatConfigUtils.getWechatCache(WxAliConstants.REDIS_WECHAT_CONFIG);
        String info_codeUrl= String.format(WxAliConstants.CODE_GET_ACCESS_TOKEN_URL,wechatCache.getMiniAppId(),wechatCache.getMiniAppSecret(),code);
        JSONObject resultJson = HttpsUtils.httpsRequest(info_codeUrl, "GET", null);
        if (resultJson.get("access_token")==null){
            //授权失败
            return null;
        }else {
            resultMap.put("accessToken",resultJson.get("access_token"));//次用户此次授权的网页token
            resultMap.put("expiresIn",resultJson.get("expires_in"));//有效期，秒
            resultMap.put("openid",resultJson.get("openid"));//用户openId
            resultMap.put("refresh_token",resultJson.get("refresh_token"));//用于刷新次网页授权token的必要参数，基本用不到
        }
        return resultMap;
    }

    /**
     * 用网页授权token和对于的openId获取用户信息
     * @param openid
     * @param accessToken
     * @return
     */
    @Override
    public WebWxUserInfo getWxUserInfo(Object openid, Object accessToken) {
        //此处限制get请求
        String info_codeUrl= String.format(WxAliConstants.ACCESS_OPENID_GET_USERINFO_URL,accessToken,openid);
        JSONObject resultJson = HttpsUtils.httpsRequest(info_codeUrl, "GET", null);
        WebWxUserInfo webWxUserInfo = new WebWxUserInfo();
        if (resultJson.get("openid")!=null){
            webWxUserInfo.setOpenid(resultJson.get("openid").toString());
        }else {
            return null;
        }
        if (resultJson.get("nickname")!=null){
            webWxUserInfo.setNickname(resultJson.get("nickname").toString());
        }else {
            return null;
        }
        if (resultJson.get("sex")!=null){
            webWxUserInfo.setSex((int)resultJson.get("sex"));
        }else {
            return null;
        }
        if (resultJson.get("province")!=null){
            webWxUserInfo.setProvince(resultJson.get("province").toString());
        }
        if (resultJson.get("city")!=null){
            webWxUserInfo.setCity(resultJson.get("city").toString());
        }
        if (resultJson.get("country")!=null){
            webWxUserInfo.setCountry(resultJson.get("country").toString());
        }
        if (resultJson.get("headimgurl")!=null){
            webWxUserInfo.setHeadimgurl(resultJson.get("headimgurl").toString());
        }
        if (resultJson.get("unionid")!=null){
            webWxUserInfo.setUnionid(resultJson.get("unionid").toString());
        }
        return webWxUserInfo;
    }

    /**
     * 小程序code登录凭证校验
     * @return
     */
    @Override
    public WxUserInfoDTO getMiniCodeSession(String code) {
        WechatConfig wechatCache = WechatConfigUtils.getWechatCache(WxAliConstants.REDIS_WECHAT_CONFIG);
        String mini_authCheck= String.format(WxAliConstants.MINI_AUTH_CODE_CHECK_URL,wechatCache.getMiniAppId(),wechatCache.getMiniAppSecret(),code);
        JSONObject resultJson = HttpsUtils.httpsRequest(mini_authCheck, "GET", null);
        WxUserInfoDTO dto=new WxUserInfoDTO();
        if (StringUtils.isNotNull(resultJson.get("openid"))){
            //请求成功
            dto.setOpenid(resultJson.get("openid").toString());
            dto.setSession_key(resultJson.get("session_key").toString());
            return dto;
        }else {
            //请求失败
            dto.setErrmsg(resultJson.get("errmsg").toString());
            return dto;
        }
    }

    /**
     * 小程序微信用户信息解密
     * code换取sessionKey
     * iv
     *  encryptedData
     */
    @Override
    public ApiResult unEncryptedIngo(String code, String iv, String encryptedData) {
        WxUserInfoDTO miniCodeSession = getMiniCodeSession(code);
        byte[] dataByte = Base64.decode(encryptedData);
        byte[] keyByte = Base64.decode(miniCodeSession.getSession_key());
        byte[] ivByte = Base64.decode(iv);
        try {
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base
                        + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters
                    .getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String dec = new String(resultByte, "UTF-8");
                // 解析出用户数据
                Map<String, Object> wxinfo = (Map) JSON.parse(dec);
                wxinfo.get("purePhoneNumber").toString();
                return ApiResult.success(wxinfo);
            } else {
                return ApiResult.error("手机号缺失");
            }
        } catch (Exception e){
            e.printStackTrace();
            throw new AppException(e.getMessage());
        }
    }

    @Override
    public ApiResult getUserPhoneNumber(String phoneCode) {
        try {
            ApiResult accessTokenAjax=getApiAccessToken();
            AccessToken data = (AccessToken)accessTokenAjax.getData();
            String api_access_token_url= String.format(WxAliConstants.MINI_PHONE_ACCESS_TOKEN_URL,data.getAccessToken());
            Map<String, String> params = new HashMap<String, String>();
            params.put("code", phoneCode);
            ObjectMapper jsonType = new ObjectMapper();
            JSONObject jsonObject = HttpsUtils.httpsRequest(api_access_token_url, "POST", jsonType.writeValueAsString(params));
            return ApiResult.success(jsonObject);
        } catch (ParseException | JsonProcessingException e) {
            e.printStackTrace();
            throw new AppException(e.getMessage());
        }
    }

    /**
     * 获取微信平台凭证accessToken,
     */
    @Override
    public ApiResult getApiAccessToken() throws ParseException {
        //调接口需要access_token，鉴于access_token的调用次数有限制，保存每一次授权获取到的token，直至失效再调二次，所以……
        WechatConfig wechatCache = WechatConfigUtils.getWechatCache(WxAliConstants.REDIS_WECHAT_CONFIG);
        AccessToken accessToken=WechatConfigUtils.getWechatAccessToken(WxAliConstants.REDIS_WECHAT_ACCESS_TOKEN);
            if (accessToken==null){
                //没有授权过，未能获取到凭证，调接口获取
                String api_access_token_url= String.format(WxAliConstants.APPID_SECURT_ACCESS_TOKEN_URL);
                Map<String, String> params = new HashMap<String, String>();
                params.put("grant_type", "client_credential");
                params.put("appid", wechatCache.getMiniAppId());
                params.put("secret", wechatCache.getMiniAppSecret());
                ObjectMapper jsonType = new ObjectMapper();
                JSONObject jsonObject = null;
                try {
                    jsonObject = HttpsUtils.httpsRequest(api_access_token_url, "POST", jsonType.writeValueAsString(params));
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                if (null == jsonObject || StringUtils.isBlank(jsonObject.getString("access_token"))) {
                    return ApiResult.error("acessToken获取失败:"+jsonObject.getString("errcode")+"->"+jsonObject.get("errmsg"));
                } else {
                    String access_token=jsonObject.getString("access_token");
                    String expires_in=jsonObject.getString("expires_in");
                    accessToken=new AccessToken();
                    accessToken.setAccessToken(access_token);
                    accessToken.setExpiresIn(expires_in);
                    accessToken.setCreateTime(new Date());
                    WechatConfigUtils.setWechatAccessToken(WxAliConstants.REDIS_WECHAT_ACCESS_TOKEN,accessToken);
                    return ApiResult.success(accessToken);
                }
            }else {
                //检验accesstoken的时间是否过期
                Boolean isExpries=WechatConfigUtils.isExpries(accessToken.getCreateTime());
                if (!isExpries){
                    //过期,重新获取
                    String api_access_token_url= String.format(WxAliConstants.APPID_SECURT_ACCESS_TOKEN_URL);
                    Map<String, String> params = new HashMap<String, String>();
                    params.put("grant_type", "client_credential");
                    params.put("appid", wechatCache.getMiniAppId());
                    params.put("secret", wechatCache.getMiniAppSecret());
                    ObjectMapper jsonType = new ObjectMapper();
                    JSONObject jsonObject = null;
                    try {
                        jsonObject = HttpsUtils.httpsRequest(api_access_token_url, "POST", jsonType.writeValueAsString(params));
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                    }
                    if (null == jsonObject || StringUtils.isBlank(jsonObject.getString("access_token"))) {
                        return ApiResult.error("acessToken过期！并且重新获取失败:"+jsonObject.getString("errcode")+"->"+jsonObject.get("errmsg"));
                    } else {
                        String access_token=jsonObject.getString("access_token");
                        String expires_in=jsonObject.getString("expires_in");
                        accessToken=new AccessToken();
                        accessToken.setAccessToken(access_token);
                        accessToken.setExpiresIn(expires_in);
                        accessToken.setCreateTime(new Date());
                        WechatConfigUtils.clearWechatAccessToken();//清楚redis中redis的缓存，重新设置
                        WechatConfigUtils.setWechatAccessToken(WxAliConstants.REDIS_WECHAT_ACCESS_TOKEN,accessToken);
                        return ApiResult.success(accessToken);
                    }
                }else {
                    //未过期，直接可用token
                    return ApiResult.success(accessToken);
                }
            }
    }

}
